using System;
using System.Reflection;
using System.Collections.Generic;

using Taimila.Entify.Sqlite;

namespace Taimila.Entify.UnitTests
{
	/// <summary>
	/// This class provides helper methods for unit tests.
	/// </summary>
	internal static class TestHelper
	{
		/// <summary>
		/// This method allows test code to inject a new value to the private field of any object.
		/// </summary>
		/// <param name="target">
		/// Target object which field value we set.<see cref="T"/>
		/// </param>
		/// <param name="fieldName">
		/// Name of the field we want to inject.<see cref="System.String"/>
		/// </param>
		/// <param name="value">
		/// New value for the field.<see cref="System.Object"/>
		/// </param>
		public static void InjectValue<T>(ref T target, string fieldName, object value)
		{
			Type t = target.GetType();
			FieldInfo fi = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
			fi.SetValue(target, value);
		}
		
		/// <summary>
		/// Gets non-public method from given type. Method returns MethodInfo
		/// which allows invoking the returned method. This way test code
		/// is able to call private and protected methods as well. Notice
		/// that static methods are not supported!
		/// </summary>
		/// <param name="methodName">
		/// Name of the method. <see cref="System.String"/>
		/// </param>
		/// <param name="t">
		/// Type that has the method. <see cref="Type"/>
		/// </param>
		/// <returns>
		/// MethodInfo presentation of the method. <see cref="MethodInfo"/>
		/// </returns>
		public static MethodInfo GetMethodFromType(string methodName, Type t)
		{
			return t.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
		}
		
		/// <summary>
		/// Get SqlitePersistentStore object that is configured to work in-mermory.
		/// </summary>
		/// <returns>
		/// SqliteStore that works in-memory. <see cref="SqliteStore"/>
		/// </returns>
		public static SqlitePersistentStore GetInMemorySQLitePersistentStore()
		{
			SqliteFactory factory = new SqliteFactory("URI=file::memory:,version=3");
			return (SqlitePersistentStore) factory.CreatePersistentStore();
		}
		
		/// <summary>
		/// Get SqliteTypeCoordinator object that is configured to work in-mermory.
		/// </summary>
		/// <returns>
		/// SqliteStore that works in-memory. <see cref="SqliteStore"/>
		/// </returns>
		public static SqliteTypeCoordinator GetInMemorySqliteTypeCoordinator()
		{
			SqliteFactory factory = new SqliteFactory("URI=file::memory:,version=3");
			return (SqliteTypeCoordinator) factory.CreateTypeCoordinator();
		}
		
		/// <summary>
		/// Capsulates given entities into entity array. This makes it easier to
		/// test persistent store.
		/// </summary>
		/// <param name="entity">
		/// Entity to be capsulated.
		/// </param>
		/// <returns>
		/// Enitity array that contains given entities.
		/// </returns>
		public static T[] CapsulteToArray<T>(params T[] entities)
		{
			return entities; // Taking advantage of C# sugar :)
		}
		
		/// <summary>
		/// Get SqlitePersistentStore and SqliteTypeCoordinator with shared in-memory database.
		/// This allows to use both classes on same data model.
		/// </summary>
		/// <returns>
		/// A <see cref="KeyValuePair<SqlitePersistentStore, SqliteTypeCoordinator>"/>
		/// </returns>
		public static KeyValuePair<SqlitePersistentStore, SqliteTypeCoordinator> GetSqlitePersistentStoreWithTypeCoordinator()
		{
			SqliteFactory factory = new SqliteFactory("URI=file::memory:,version=3");
			SqlitePersistentStore persistentStore = (SqlitePersistentStore) factory.CreatePersistentStore();
			SqliteTypeCoordinator typeCoordinator = (SqliteTypeCoordinator) factory.CreateTypeCoordinator();

			return new KeyValuePair<SqlitePersistentStore, SqliteTypeCoordinator>(persistentStore, typeCoordinator);
		}
	}
}
